import type { AxiosRequestConfig, Method } from 'axios'
import axios from 'axios'
import { ElLoading, ElMessage, type LoadingOptions } from 'element-plus'
import { auth } from '@/plugins/store/user'
import router from '@/router'
const pendingMap = new Map()
const loadingInstance: LoadingInstance = {
  target: null,
  count: 0,
}

/**
 * 根据运行环境获取基础请求URL
 */
export const getUrl = (): string => {
  const value: string = import.meta.env.VITE_AXIOS_BASE_URL as string
  return value == 'getCurrentDomain' ? window.location.protocol + '//' + window.location.host : value
}

/**
 * 根据运行环境获取基础请求URL的端口
 */
export const getUrlPort = (): string => {
  const url = getUrl()
  return new URL(url).port
}

/**
 * 创建`Axios`
 * 默认开启`reductDataFormat(简洁响应)`,返回类型为`ApiPromise`
 * 关闭`reductDataFormat`,返回类型则为`AxiosPromise`
 */
function createAxios<Data = any, T = ApiPromise<Data>>(axiosConfig: AxiosRequestConfig, options: Options = {}, loading: LoadingOptions = {}): T {
  const adminInfo = auth()
  const Axios = axios.create({
    baseURL: getUrl(),
    // timeout: 1000 * 10,
    headers: {
      server: true,
    },
    responseType: 'json',
  })

  // 合并默认请求选项
  options = Object.assign(
    {
      CancelDuplicateRequest: true, // 是否开启取消重复请求, 默认为 true
      loading: false, // 是否开启loading层效果, 默认为false
      reductDataFormat: true, // 是否开启简洁的数据结构响应, 默认为true
      showErrorMessage: true, // 是否开启接口错误信息展示,默认为true
      showCodeMessage: true, // 是否开启code不为1时的信息提示, 默认为true
      showSuccessMessage: false, // 是否开启code为1时的信息提示, 默认为false
      anotherToken: '', // 当前请求使用另外的用户token
    },
    options
  )

  // 请求拦截
  Axios.interceptors.request.use(
    (config) => {
      removePending(config)
      options.CancelDuplicateRequest && addPending(config)
      // 创建loading实例
      if (options.loading) {
        loadingInstance.count++
        if (loadingInstance.count === 1) {
          loadingInstance.target = ElLoading.service(loading)
        }
      }
      // 自动携带token
      if (config.headers) {
        const token = adminInfo.getToken()
        if (token) (config.headers as anyObj).token = token
      }
      return config
    },
    (error) => {
      return Promise.reject(error)
    }
  )

  // 响应拦截
  Axios.interceptors.response.use(
    (response) => {
      removePending(response.config)
      options.loading && closeLoading(options) // 关闭loading
      if (response.config.responseType == 'json') {
        if (response.data && response.data.code !== HttpAsk.SUCCESS) {
          if (options.showCodeMessage) {
            ElMessage({
              type: 'error',
              message: response.data.message,
              zIndex: 9999,
            })
            // code不等于1, 页面then内的具体逻辑就不执行了
            return Promise.reject(response.data)
          }
          // 自动跳转到路由name或path
          if (response.data.code == 302) {
            router.push({ path: response.data.data.routePath ?? '', name: response.data.data.routeName ?? '' })
          }
        } else if (options.showSuccessMessage && response.data && response.data.code == HttpAsk.SUCCESS) {
          ElMessage({
            message: response.data.message ? response.data.message : '操作成功',
            type: 'success',
            zIndex: 9999,
          })
        }
      }

      return options.reductDataFormat ? response.data : response
    },
    (error) => {
      console.log(error)
      error.config && removePending(error.config)
      options.loading && closeLoading(options) // 关闭loading
      options.showErrorMessage && httpErrorStatusHandle(error) // 处理错误状态码
      return Promise.reject(error) // 错误继续返回给到具体页面
    }
  )
  return Axios(axiosConfig) as T
}

export default createAxios

/**
 * 处理异常
 * @param {*} error
 */
function httpErrorStatusHandle(error: any) {
  // 处理被取消的请求
  if (axios.isCancel(error)) return console.error('因为请求重复被自动取消：' + error.message)
  let message = ''
  if (error && error.response) {
    switch (error.response.status) {
      case 302:
        message = '接口重定向了！'
        break
      case 400:
        message = '参数不正确！'
        break
      case 403:
        message = '您没有权限操作！'
        break
      case 404:
        message = '请求地址出错:' + error.response.config.url
        break
      case 500:
        message = '服务器内部错误！'
        break
      default:
        message = '异常问题，请联系网站管理员！'
        break
    }

    if (error.response.status === 401) {
      ElMessage.error('登录过期重新登录！')
      auth().logout()
      return
    }
  }

  if (error.message.includes('timeout')) message = '网络请求超时！'
  if (error.message.includes('Network')) message = window.navigator.onLine ? '服务端异常！' : '您断网了！'

  ElMessage({
    type: 'error',
    message,
    zIndex: 9999,
  })
}

/**
 * 关闭Loading层实例
 */
function closeLoading(options: Options) {
  if (options.loading && loadingInstance.count > 0) loadingInstance.count--
  if (loadingInstance.count === 0) {
    loadingInstance.target.close()
    loadingInstance.target = null
  }
}

/**
 * 储存每个请求的唯一cancel回调, 以此为标识
 */
function addPending(config: AxiosRequestConfig) {
  const pendingKey = getPendingKey(config)
  config.cancelToken =
    config.cancelToken ||
    new axios.CancelToken((cancel) => {
      if (!pendingMap.has(pendingKey)) {
        pendingMap.set(pendingKey, cancel)
      }
    })
}

/**
 * 删除重复的请求
 */
function removePending(config: AxiosRequestConfig) {
  const pendingKey = getPendingKey(config)
  if (pendingMap.has(pendingKey)) {
    const cancelToken = pendingMap.get(pendingKey)
    cancelToken(pendingKey)
    pendingMap.delete(pendingKey)
  }
}

/**
 * 生成每个请求的唯一key
 */
function getPendingKey(config: AxiosRequestConfig) {
  let { data } = config
  const { url, method, params, headers } = config
  if (typeof data === 'string') data = JSON.parse(data) // response里面返回的config.data是个字符串对象
  return [url, method, headers && (headers as anyObj).token ? (headers as anyObj).token : '', JSON.stringify(params), JSON.stringify(data)].join('&')
}

/**
 * 根据请求方法组装请求数据/参数
 */
export function requestPayload(method: Method, data: anyObj) {
  if (method == 'GET') {
    return {
      params: data,
    }
  } else if (method == 'POST') {
    return {
      data: data,
    }
  }
}

interface LoadingInstance {
  target: any
  count: number
}
interface Options {
  // 是否开启取消重复请求, 默认为 true
  CancelDuplicateRequest?: boolean
  // 是否开启loading层效果, 默认为false
  loading?: boolean
  // 是否开启简洁的数据结构响应, 默认为true
  reductDataFormat?: boolean
  // 是否开启接口错误信息展示,默认为true
  showErrorMessage?: boolean
  // 是否开启code不为1时的信息提示, 默认为true
  showCodeMessage?: boolean
  // 是否开启code为1时的信息提示, 默认为false
  showSuccessMessage?: boolean
  // 当前请求使用另外的用户token
  anotherToken?: string
}

export const HttpAsk = {
  SUCCESS: 'success',
  FAIL: 'fail',
}
